<?php 
/**
 * This file is part of DomAr.
 * 
 * DomAr is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * DomAr is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with DomAr.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * @link http://inditel.ee/
 * @copyright 2007-2009 Inditel Meedia OÜ
 * @author Oliver Leisalu <oliver@inditel.ee>
 */

/**
* Plugin that helps to control datetimes.
*
* Internally extends to DomArDate and DomArTime. Although this is not possible in php but this class acts like it extends to two classes.
* @package DomAr
* @subpackage Plugin
*/
class DomArDateTime extends DomArDate {

	/**
	 * Time in formay H:i:s
	 * @var String
	 */
	protected $time = NULL;
	/**
	 * Time in format His
	 * @var Int
	 */
	protected $timeNum = NULL;
	/**
	 * @param String $datetime
	 */
	public function __construct( $datetime = NULL ) {
		$this->setDateTime( $datetime );
	}
	/**
	 * @param String $datetime
	 * @return Int
	 */
	public function validate( $datetime ) {
		
		if( $datetime == 0 or $datetime === NULL)
			return true;
		
		$parse = self::parseDateTime( $datetime );
		$date = $parse['year'].'-'.$parse['month'].'-'.$parse['day'];

		$timeOk = true;
		if( $parse['hour'] > 23 or $parse['hour'] < 0 ) 
			$timeOk = false;
		elseif( $parse['minute'] > 59 or $parse['minute'] < 0 ) 
			$timeOk = false;
		elseif( $parse['second'] > 59 or $parse['second'] < 0 ) 
			$timeOk = false;
			
		return DomArDate::validate( $date ) && $timeOk;
	}
	
	// -----------------------------------------------------
	// GET
	// -----------------------------------------------------
	/**
	 * @return Bool
	 */
	public function isNull() {
		return DomArDate::isNull() && $this->time == NULL;
	}
	/**
	 * @return Bool
	 */
	public function inPast() {
		return ($this->dateNum.$this->timeNum) < date('YmdHis');
	}
	/**
	 * @return Bool
	 */
	public function inFuture() {
		return !$this->inPast();
	}
	/**
	 * @param String $format
	 * @return String
	 */
	public function datetime( $format = DOMAR_DATETIME_DEFAULT_FORMAT ) {
		if( $format == 'Y-m-d H:i:s')
			return $this->isNull() ? '' : $this->date.' '.$this->time;
		else
			return self::formatDateTime( $this->date.' '.$this->time, $format );
	}
	/**
	 * @param String $format
	 * @return String
	 */
	public function time( $format = DOMAR_TIME_DEFAULT_FORMAT ) {
		if( $format == 'H:i:s')
			return $this->time;
		else
			return self::formatTime( $this->time, $format );
	}
	
	/**
	 * Returns how many hours and minutes has past from time specified. 
	 * 
	 * Sample output: <br>
	 * 1 tund 25 minutit<br>
	 * 12 tundi 32 minutit<br>
	 *
	 * @return String
	 */
	public function timeLeft() {
		$aeg = $this->stamp();
		$aeg = $aeg - strtotime("now");
		
		if( $aeg < 0 )
			return '-'; 
		
		$hours = floor($aeg / 3600 );
		$minutes = floor($aeg%3600 / 60);
			
		return ($hours > 0 ? ($hours.($hours == 1 ? 'tund ' : ' tundi ')) : NULL) . ($minutes > 0 ? $minutes.($minutes == 1 ? 'minut ' : ' minutit ') : NULL);	
	}
	/**
	 * @return String
	 */
	public function hour() {
		return substr($this->timeNum,0,2);
	}
	/**
	 * @return String
	 */
	public function minute() {
		return substr($this->timeNum,2,2);
	}
	/**
	 * @return String
	 */
	public function second() {
		return substr($this->timeNum,4,2);
	}
	
	
	/**
	 * @return Int
	 */
	public function dateTimeNum() {
		return (int)$this->dateNum.$this->timeNum;
	}
	/**
	 * @return Int
	 */
	public function timeNum() {
		return (int)$this->timeNum;
	}
	/**
	 * @return Int
	 */
	public function stamp() {
		if( $this->date == 0 && $this->time == 0)
			return 0;
		
		$parse = self::parseDateTime( $this->date.' '.$this->time );
		return mktime( $parse['hour'], $parse['minute'], $parse['second'], $parse['month'], $parse['day'], $parse['year']);
	}
	/**
	 * @param String $var
	 * @return Mixed
	 */
	public function __get( $var ) {
		switch( $var ) {
			case 'datetimeNum':
				return $this->datetimeNum();
			break;
			case 'datetime':
				return $this->datetime();
			break;
			case 'stamp':
				return $this->stamp();
			break;
			case 'isNull':
				return $this->isNull();
			break;
			case 'inPast':
				return $this->inPast();
			break;
			case 'inFuture':
				return $this->inFuture();
			break;
			
			
			case 'dateNum':
				return $this->dateNum();
			break;
			case 'date':
				return $this->date();
			break;
			case 'year':
				return $this->year();
			break;
			case 'month':
				return $this->month();
			break;
			case 'day':
				return $this->day();
			break;
			
			
			
			case 'timeNum':
				return $this->timeNum();
			break;
			case 'time':
				return $this->time();
			break;
			case 'hour':
				return $this->hour();
			break;
			case 'minute':
				return $this->minute();
			break;
			case 'second':
				return $this->second();
			break;
			
			default:
				throw new Exception('Can\'t access non existing property '.$var.'');
			break;
		}
	}
	
	// -----------------------------------------------------
	// SET
	// -----------------------------------------------------
	/**
	 * @param String $datetime
	 */
	public function setDateTime( $datetime) {
		
		$this->changed();
		
		//set null
		if( $datetime == '' ) {
			$this->date = NULL;
			$this->dateNum =  NULL;
			$this->time = NULL;
			$this->timeNum = NULL;
			return;
		
		// set now	
		} else if( $datetime === 'now' )	
			$datetime = date('Y-m-d H:i:s');
		
		else if ( is_numeric($datetime) ) 
			$datetime = date('Y-m-d H:i:s', $datetime);
	
		else if( self::validate( $datetime ) === false )
			throw new InvalidArgumentException('Invalid datetime format <strong>'.$datetime.'</strong>');
		
		
		$parse = self::parseDateTime( $datetime );

		$this->date = $parse['year'].'-'.$parse['month'].'-'.$parse['day'];
		$this->dateNum = $parse['year'].$parse['month'].$parse['day'];
		
		$this->time = $parse['hour'].':'.$parse['minute'].':'.$parse['second'];
		$this->timeNum = $parse['hour'].$parse['minute'].$parse['second'];
	}
	/**
	 * @param String $time
	 */
	public function setTime( $time ) {
		
		$this->changed();
		
		// set now
		if( $time === 'now' )
			$time = date('H:i:s');
		
		// is timestamp
		else if( is_numeric($time) === true )
			$time = date('H:i:s', $time );
		
		else if( $this->validate( $time ) === false )
			throw new InvalidArgumentException('Invalid time format <strong>'.$time.'</strong>');
		
		$parse = self::parseDateTime( '0000-00-00 '.$time );
		$this->time = $parse['hour'].':'.$parse['minute'].':'.$parse['second'];
		$this->timeNum = $parse['hour'].$parse['minute'].$parse['second'];
	}
	/**
	 * @param String $var
	 * @param Mixed $value
	 */
	public function __set( $var, $value ) {
		switch( $var ) {
			case 'datetime':
				$this->setDateTime($value);
			break;
			case 'stamp':
				$this->setDateTime( $value );
			break;
			case 'date':
				$this->setDate($value);
			break;
			case 'time':
				$this->setTime($value);
			break;
			default:
				throw new Exception('Can\'t access non existing property '.$var.'');
			break;
		}
	}
	
	
	// -----------------------------------------------------
	// HELPERS
	// -----------------------------------------------------

	/**
	* Converts date to format
	*
	* For allowed format letters see formatTime and formatDate. Both these methods letters can be used.
	*
	* @param String $date Format H:i:s
	* @param String $format to which date will be converted.
	*/
	public static function formatDateTime( $datetime, $format ) {
		$format = self::formatDate( $datetime, $format );
		$format = self::formatTime( $datetime, $format );
		return $format;
	}
	
	/**
	* Converts time to format
	*
	* Allowed format letters
	* <ul>
	* <li> H - hour</li>
	* <li> i - minute</li>
	* <li> s - second/li>
	* </ul>
	*
	* @param String $date Format H:i:s
	* @param String $format to which date will be converted.
	*/
	public static function formatTime( $time, $format ) {

		$parse = self::parseDateTime( $time );
		$newTime = str_replace('H',$parse['hour'],$format);
		$newTime = str_replace('i',$parse['minute'],$newTime);
		$newTime = str_replace('s',$parse['second'],$newTime);
		
		return $newTime;
	}
	
	/**
	* Same as DomArDate::parseDate
	*/
	public static function parseDateTime( $date ) {
		return DomArDate::parseDate( $date );
	}
	/**
	 * @return String
	 */
	public function __toString() {
		return $this->isNull() ? '' : $this->date.' '.$this->time;
	}
}


/**
* @package DomAr
* @subpackage Plugin
*/
class DomArProperty_DomArDateTime extends DomArProperty_Plugin implements IDomArPropertyAlterTableListener,IDomArPropertyCompareListener,IDomArPropertyInsertListener,IDomArPropertyUpdateListener,IDomArPropertyBeforeSetListener {
	
	protected $defaultValue;
	
	public function __construct( $name, $defaultValue = NULL ) {
		parent::__construct( $name );
		$this->defaultValue = $defaultValue;
	}
	
	public function init( DomArObject $object, array &$values ) {
		return (string)$values[$this->name];
	}
	
	public function load( DomArObject $object, &$propertyValue ) {
		
		$d = new DomArDateTime( ($propertyValue !== NULL ? $propertyValue : ($this->defaultValue ? $this->defaultValue : NULL)) );
		$d->changeParent( $object, $this );
		return $d;
	}
	
	public function compare( DomArObject $object, &$oldPropertyValue, &$newPropertyValue ) {
		$a  = is_object($newPropertyValue) ? $newPropertyValue : new DomArDateTime($newPropertyValue);
		return $oldPropertyValue->datetime() === $a->datetime();
	}
	
	public function beforeSet( DomArObject $object, &$oldPropertyValue, &$newPropertyValue ) {

		if( is_object($newPropertyValue) ) {
			$newPropertyValue->changeParent( $object, $this );
		} else {
			$newPropertyValue = new DomArDateTime( $newPropertyValue);
			$newPropertyValue->changeParent( $object, $this );
		}

	}
	
	public function isLoaded( DomArObject $object, &$propertyValue ) {
		return is_object($propertyValue);
	}
	
	public function validate( DomArObject $object, &$propertyValue, array &$customParams = array() ) {
		return (is_object($propertyValue) && get_class($propertyValue) == 'DomArDateTime' or is_string($propertyValue) && DomArDateTime::validate($propertyValue) or $propertyValue === NULL or is_numeric($propertyValue)) ? 0 : 1;
	}
	
	public function insert( DomArObject $object, &$propertyValue, DomArValueSet $set  ) {
		$propertyValue = $object->{$this->name};
		if( is_object($propertyValue) )
			$value = ($propertyValue->isNull() ? 'NULL' : '\''.$propertyValue->datetime('Y-m-d H:i:s').'\'');
		else
			$value = ($propertyValue == '' ? 'NULL' : '\''.$propertyValue.'\'');
			
		$set->set( $this->name, $value );
	}
	
	public function update( DomArObject $object, &$propertyValue, DomArValueSet $set ) {
		$set->set( $this->name, ($propertyValue->isNull() ? 'NULL' : '\''.$propertyValue->datetime('Y-m-d H:i:s').'\'') );
	}
	
	public function alterTable( DomArTable $table ) {
		return $table->changeColumn( $this->name, 'datetime', true );
	}
}
?>